# package api

# import (
#     "fmt"
#     "net/http"
#     "os"
#     "path/filepath"
#     "strings"
#     "text/template"

#     "github.com/aptly-dev/aptly/aptly"
#     "github.com/aptly-dev/aptly/database"
#     "github.com/aptly-dev/aptly/deb"
#     "github.com/aptly-dev/aptly/query"
#     "github.com/aptly-dev/aptly/task"
#     "github.com/aptly-dev/aptly/utils"
#     "github.com/gin-gonic/gin"
# )
from database.goleveldb.db import packageList
from deb.local import  *
from context import context
from deb.local import *
from deb.listp import *
from api.files import *
from api.api import *


# // GET /repos
def reposListInAPIMode(localRepos  ) :
    pass
    # return func(c *gin.Context) {
    #     c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
    #     c.Writer.Flush()
    #     c.Writer.WriteString("<pre>\n")
    #     if len(localRepos) == 0 {
    #         c.Writer.WriteString("<a href=\"-/\">default</a>\n")
    #     }
    #     for publishPrefix := range localRepos {
    #         c.Writer.WriteString(fmt.Sprintf("<a href=\"%[1]s/\">%[1]s</a>\n", publishPrefix))
    #     }
    #     c.Writer.WriteString("</pre>")
    #     c.Writer.Flush()
    # }


# // GET /repos/:storage/*pkgPath
def reposServeInAPIMode(c :dict) :
    pkgpath = c.get("pkgPath")

    storage = c.get("storage")
    if storage == "-" :
        storage = ""
    else :
        storage = "filesystem:" + storage
    

    publicPath = context.GetPublishedStorage(storage).PublicPath()
    # c.FileFromFS(pkgpath, http.Dir(publicPath))
    raise '未完善的接口'



# // GET /api/repos
def apiReposList() :
    result = []
    aptlyContext=context.AptlyContext()
    collectionFactory = aptlyContext.NewCollectionFactory()
    collection = collectionFactory.LocalRepoCollection()
    collection.ForEach(lambda r:result.append(r))
    return  result
    # c.JSON(200, result)

# // POST /api/repos
def apiReposCreate(b:dict) :
    # var b struct {
    #     Name                string `binding:"required"`
    #     Comment             string
    #     DefaultDistribution string
    #     DefaultComponent    string
    # }

    aptlyContext=context.AptlyContext()

    repo = NewLocalRepo(b.get('Name'), b.get('Comment'))
    repo.DefaultComponent = b.get('DefaultComponent')
    repo.DefaultDistribution = b.get('DefaultDistribution')

    collectionFactory = aptlyContext.NewCollectionFactory()
    collection = collectionFactory.LocalRepoCollection()
    _, err=collection.Add(repo)
    if err is not None :
        raise Exception(err)
#     if err is not None  {
#         c.AbortWithError(400, err)
#         return
#     }

#     c.JSON(201, repo)
# }

# // PUT /api/repos/:name
# def apiReposEdit(c *gin.Context) {
#     var b struct {
#         Name                *string
#         Comment             *string
#         DefaultDistribution *string
#         DefaultComponent    *string
#     }

#     if c.Bind(&b) is not None  {
#         return
#     }
# aptlyContext=context.AptlyContext()
#     collectionFactory = context.NewCollectionFactory()
#     collection = collectionFactory.LocalRepoCollection()

#     repo, err = collection.ByName(c.Params.ByName("name"))
#     if err is not None  {
#         c.AbortWithError(404, err)
#         return
#     }

#     if b.Name is not None  {
#         _, err = collection.ByName(*b.Name)
#         if err is None  {
#             // already exists
#             c.AbortWithError(404, err)
#             return
#         }
#         repo.Name = *b.Name
#     }
#     if b.Comment is not None  {
#         repo.Comment = *b.Comment
#     }
#     if b.DefaultDistribution is not None  {
#         repo.DefaultDistribution = *b.DefaultDistribution
#     }
#     if b.DefaultComponent is not None  {
#         repo.DefaultComponent = *b.DefaultComponent
#     }

#     err = collection.Update(repo)
#     if err is not None  {
#         c.AbortWithError(500, err)
#         return
#     }

#     c.JSON(200, repo)
# }

# // GET /api/repos/:name
def apiReposShow(name) :
    aptlyContext=context.AptlyContext()
    collectionFactory = aptlyContext.NewCollectionFactory()
    collection = collectionFactory.LocalRepoCollection()

    repo,err= collection.ByName(name)
    return repo

#
# // DELETE /api/repos/:name
# def apiReposDrop(c *gin.Context) {
#     force := c.Request.URL.Query().Get("force") == "1"
#     name := c.Params.ByName("name")

#     collectionFactory := context.NewCollectionFactory()
#     collection := collectionFactory.LocalRepoCollection()
#     snapshotCollection := collectionFactory.SnapshotCollection()
#     publishedCollection := collectionFactory.PublishedRepoCollection()

#     repo, err := collection.ByName(name)
#     if err is not None  {
#         AbortWithJSONError(c, 404, err)
#         return
#     }

#     resources := []string{string(repo.Key())}
#     taskName := fmt.Sprintf("Delete repo %s", name)
#     maybeRunTaskInBackground(c, taskName, resources, func(_ aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
#         published := publishedCollection.ByLocalRepo(repo)
#         if len(published) > 0 {
#             return &task.ProcessReturnValue{Code: http.StatusConflict, Value:   None }, fmt.Errorf("unable to drop, local repo is published")
#         }

#         if !force {
#             snapshots := snapshotCollection.ByLocalRepoSource(repo)
#             if len(snapshots) > 0 {
#                 return &task.ProcessReturnValue{Code: http.StatusConflict, Value:   None }, fmt.Errorf("unable to drop, local repo has snapshots, use ?force=1 to override")
#             }
#         }

#         return &task.ProcessReturnValue{Code: http.StatusOK, Value: gin.H{}}, collection.Drop(repo)
#     })
# }

# // GET /api/repos/:name/packages
def apiReposPackagesShow(c:dict) :
    aptlyContext=context.AptlyContext()
    collectionFactory = aptlyContext.NewCollectionFactory()
    collection = collectionFactory.LocalRepoCollection()

    repo, err = collection.ByName(c.get("name"))
    if err is not None  :
        raise Exception(err)
        
    

    _,err = collection.LoadComplete(repo)
    if err is not None  :
        raise Exception(err,'--------')
    

    showPackages(c, repo.RefList(), collectionFactory)


# // Handler for both add and delete
def apiReposPackagesAddDelete(c:dict, taskNamePrefix :str, cb) :
    # var b struct {
    #     PackageRefs []string
    # }

    # if c.Bind(&b) is not None  {
    #     return
    # }
    aptlyContext=context.AptlyContext()
    collectionFactory = aptlyContext.NewCollectionFactory()
    collection = collectionFactory.LocalRepoCollection()

    repo,err= collection.ByName(c.get("name"))
    if err is not None  :
        raise Exception(err)
        return
    

    collection.LoadComplete(repo)
    # if err is not None  {
    #     c.AbortWithError(500, err)
    #     return
    # }

    resources = [repo.Key()]

    print("Loading packages...\n")
    packageList,err= NewPackageListFromRefList(repo.RefList(), collectionFactory.PackageCollection(), None)
    # if err is not None  {
    #     return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: None}, err
    # }

    # // verify package refs and build package list
    for ref in c.get('PackageRefs') :
        print('Info:',taskNamePrefix+repo.Name)
        p :Package=None

        p= collectionFactory.PackageCollection().ByKey(ref)
        # if err is not None  {
        #     if err == database.ErrNotFound {
        #         return &task.ProcessReturnValue{Code: http.StatusNotFound, Value: None}, fmt.Errorf("packages %s: %s", ref, err)
        #     }

        #     return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: None}, err
        # }
        cb(packageList, p, '')
    #     if err is not None  {
    #         return &task.ProcessReturnValue{Code: http.StatusBadRequest, Value: None}, err
    #     }
    # }

    repo.UpdateRefList(NewPackageRefListFromPackageList(list))

    collectionFactory.LocalRepoCollection().Update(repo)


# // POST /repos/:name/packages
def apiReposPackagesAdd(c :dict) :
    aptlyContext=context.AptlyContext()
    def NoName(packageList :PackageList, p :Package, out):
        print("Adding package %s\n", p.Name)
        return packageList.Add(p)
    apiReposPackagesAddDelete(c, "Add packages to repo ", NoName)


# // DELETE /repos/:name/packages
def apiReposPackagesDelete(c :dict) :
    aptlyContext=context.AptlyContext()
    def NoName(lists :PackageList, p :Package, out):
        print("Removing package %s\n", p.Name)
        lists.Remove(p)
        return None

    apiReposPackagesAddDelete(c, "Delete packages from repo ", NoName)


# // POST /repos/:name/file/:dir/:file
def apiReposPackageFromFile(c :dict) :
    aptlyContext=context.AptlyContext()
    # // redirect all work to dir method
    apiReposPackageFromDir(c)

from deb.importp import *
from utils.listup import *
# // POST /repos/:name/file/:dir
def apiReposPackageFromDir(c :dict) :
    aptlyContext=context.AptlyContext()
    forceReplace = c.get("forceReplace") == "1"
    noRemove = c.get("noRemove") == "1"

    if not verifyDir(c) :
        return
    

    dirParam = c.get("dir")
    fileParam = c.get("file")
    if fileParam != "" and not verifyPath(fileParam) :
        raise Exception("wrong file")
        
    

    collectionFactory = aptlyContext.NewCollectionFactory()
    collection = collectionFactory.LocalRepoCollection()

    name = c.get("name")
    repo,err= collection.ByName(name)
    if err is not None  :
        raise Exception(err)
        
    

    err = collection.LoadComplete(repo)
    # if err is not None  {
    #     c.AbortWithError(500, err)
    #     return
    # }

    taskName :str=''
    sources :List[str]=[]
    if fileParam == "" :
        taskName =("Add packages from dir {} to repo {}".format(dirParam, name))
        sources = [os.path.join(aptlyContext.UploadPath(), dirParam)]
    else :
        sources = [os.path.join(aptlyContext.UploadPath(), dirParam, fileParam)]
        taskName = ("Add package %s from dir %s to repo %s", fileParam, dirParam, name)
    

    resources = [repo.Key()]
    resources = resources+sources
    verifier = aptlyContext.GetVerifier()

        
    packageFiles=[]
    failedFiles=[]
    otherFiles=[]
    processedFiles=[]
    failedFiles2=[]
    reporter= dict(Warnings=[],AddedLines=[],RemovedLines= [])
        
    
    packageList :PackageList=None
    

    packageFiles, otherFiles, failedFiles = CollectPackageFiles(sources, reporter)
    packageList,err= NewPackageListFromRefList(repo.RefList(), collectionFactory.PackageCollection(), None)
    if err is not None :
        raise Exception(err)

    processedFiles, failedFiles2= ImportPackageFiles(packageList, packageFiles, forceReplace, verifier, aptlyContext.PackagePool(),
        collectionFactory.PackageCollection(), reporter, None, collectionFactory.ChecksumCollection)
    failedFiles = failedFiles+failedFiles2
    processedFiles = processedFiles+ otherFiles

    # if err is not None  {
    #     return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: None}, fmt.Errorf("unable to import package files: %s", err)
    # }
    repo.UpdateRefList(NewPackageRefListFromPackageList(packageList))

    err = collectionFactory.LocalRepoCollection().Update(repo)
    # if err is not None  {
    #     return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: None}, fmt.Errorf("unable to save: %s", err)
    # }

    if not noRemove :
        processedFiles = StrSliceDeduplicate(processedFiles)
        
        for  file in processedFiles :
            err = os.remove(file)
            if err is not None  :
                print("Warning:unable to remove file {}:".format (file))


        # // atempt to remove dir, if it fails, that's fine: probably it's not empty
        os.rmdir(os.path.join(aptlyContext.UploadPath(), dirParam))
    

    if failedFiles is None  :
        failedFiles = []
    

    if len(reporter['AddedLines']) > 0 :
        print("Added: {}\n".format( ", ".join(reporter['AddedLines'])))
    
    if len(reporter['RemovedLines']) > 0 :
        print("Removed: {}\n".format(", ".join(reporter['RemovedLines'] )))
    
    if len(reporter['Warnings']) > 0 :
        print("Warnings: {}\n".format(", ".join(reporter['Warnings'])))
    
    if len(failedFiles) > 0 :
        print("Failed files: {}\n".format(", ".join(failedFiles)) )
    
    print(reporter,failedFiles)



# // POST /repos/:name/include/:dir/:file
# def apiReposIncludePackageFromFile(c *gin.Context) {
#     // redirect all work to dir method
# aptlyContext=context.AptlyContext()
#     apiReposIncludePackageFromDir(c)
# }

# // POST /repos/:name/include/:dir
# def apiReposIncludePackageFromDir(c *gin.Context) {
#     forceReplace := c.Request.URL.Query().Get("forceReplace") == "1"
#     noRemoveFiles := c.Request.URL.Query().Get("noRemoveFiles") == "1"
#     acceptUnsigned := c.Request.URL.Query().Get("acceptUnsigned") == "1"
#     ignoreSignature := c.Request.URL.Query().Get("ignoreSignature") == "1"

#     repoTemplateString := c.Params.ByName("name")
#     collectionFactory := context.NewCollectionFactory()

#     if !verifyDir(c) {
#         return
#     }

#     var sources []string
#     var taskName string
#     dirParam := c.Params.ByName("dir")
#     fileParam := c.Params.ByName("file")
#     if fileParam != "" && !verifyPath(fileParam) {
#         AbortWithJSONError(c, 400, fmt.Errorf("wrong file"))
#         return
#     }

#     if fileParam == "" {
#         taskName = fmt.Sprintf("Include packages from changes files in dir %s to repo matching template %s", dirParam, repoTemplateString)
#         sources = []string{filepath.Join(context.UploadPath(), dirParam)}
#     } else {
#         taskName = fmt.Sprintf("Include packages from changes file %s from dir %s to repo matching template %s", fileParam, dirParam, repoTemplateString)
#         sources = []string{filepath.Join(context.UploadPath(), dirParam, fileParam)}
#     }

#     repoTemplate, err := template.New("repo").Parse(repoTemplateString)
#     if err is not None  {
#         AbortWithJSONError(c, 400, fmt.Errorf("error parsing repo template: %s", err))
#         return
#     }

#     var resources []string
#     if len(repoTemplate.Tree.Root.Nodes) > 1 {
#         resources = append(resources, task.AllLocalReposResourcesKey)
#     } else {
#         // repo template string is simple text so only use resource key of specific repository
#         repo, err := collectionFactory.LocalRepoCollection().ByName(repoTemplateString)
#         if err is not None  {
#             AbortWithJSONError(c, 404, err)
#             return
#         }

#         resources = append(resources, string(repo.Key()))
#     }
#     resources = append(resources, sources...)

#     maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
#         var (
#             err                       error
#             verifier                  = context.GetVerifier()
#             changesFiles              []string
#             failedFiles, failedFiles2 []string
#             reporter                  = &aptly.RecordingResultReporter{
#                 Warnings:     []string{},
#                 AddedLines:   []string{},
#                 RemovedLines: []string{},
#             }
#         )

#         changesFiles, failedFiles = deb.CollectChangesFiles(sources, reporter)
#         _, failedFiles2, err = deb.ImportChangesFiles(
#             changesFiles, reporter, acceptUnsigned, ignoreSignature, forceReplace, noRemoveFiles, verifier,
#             repoTemplate, context.Progress(), collectionFactory.LocalRepoCollection(), collectionFactory.PackageCollection(),
#             context.PackagePool(), collectionFactory.ChecksumCollection,   None , query.Parse)
#         failedFiles = append(failedFiles, failedFiles2...)

#         if err is not None  {
#             return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value:   None }, fmt.Errorf("unable to import changes files: %s", err)
#         }

#         if !noRemoveFiles {
#             // atempt to remove dir, if it fails, that's fine: probably it's not empty
#             os.Remove(filepath.Join(context.UploadPath(), dirParam))
#         }

#         if failedFiles is  None  {
#             failedFiles = []string{}
#         }

#         if len(reporter.AddedLines) > 0 {
#             out.Printf("Added: %s\n", strings.Join(reporter.AddedLines, ", "))
#         }
#         if len(reporter.RemovedLines) > 0 {
#             out.Printf("Removed: %s\n", strings.Join(reporter.RemovedLines, ", "))
#         }
#         if len(reporter.Warnings) > 0 {
#             out.Printf("Warnings: %s\n", strings.Join(reporter.Warnings, ", "))
#         }
#         if len(failedFiles) > 0 {
#             out.Printf("Failed files: %s\n", strings.Join(failedFiles, ", "))
#         }

#         return &task.ProcessReturnValue{Code: http.StatusOK, Value: gin.H{
#             "Report":      reporter,
#             "FailedFiles": failedFiles,
#         }},   None 

#     })
# }